#!/usr/bin/env python
# @file interactive_session.py


import atexit
import logging
import os
import select
import sys
import termios

import parse_context

from common_helpers import *


class InputBuffer:

    def __init__(self, session):
        self._buffer = ''
        self._session = session
        logging.info('InputBuffer created')

    def add(self, c):
        self._buffer += c
        o = ord(c)
        logging.info('%s char ord: 0x%x' % (classname(self), o))

        if o == 0x04: # EOT (End of Transmission) character
            raise parse_context.DoneParsing

        if c == '\n' or c == '\r':
            self._eval()
            self._buffer = ''
            return

        self._session.write_output(c)

    def _eval(self):
        logging.warning('%s eval not implemented' % classname(self))


class InteractiveSession(parse_context.ParseContext):

    _RC_FILES = (
        '/etc/varixrc',
        '%{env:HOME}/.varixrc',
    )

    _TIMEOUT = 10000

    def __init__(self, context):
        parse_context.ParseContext.__init__(self, context)

        self._input_buffer = InputBuffer(self)
        self._tty_init()
        self._process_rc_files()
        self._display_prompt()
        self._select()

        logging.info('InteractiveSession created')

    def _display_prompt(self):
        self.write_output(self._context.prompt())
        logging.info('%s displayed prompt' % classname(self))

    def _parse_char(self, char):
        self._input_buffer.add(char)

    def _process_rc_files(self):
        for i in self._RC_FILES:
            pass

    def _restore_term(self):
        """
        Restores input tty to a state which was previously saved in the 
        instantiation internally.
        """
        fd = sys.stdin.fileno()
        when = termios.TCSANOW # Change immediately.
        attributes = self._term_save
        termios.tcsetattr(fd, when, attributes)

    def _select(self):
        logging.info('%s running infinite select loop' % classname(self))

        timeout = self._TIMEOUT

        while True:
            logging.info('%s select iteration' % classname(self))

            # These assignments have to be in-loop due to the Session's 
            # ignorance of changes in context during execution.
            readers, writers, errs = [sys.stdin.fileno()], [], []
            wd = os.getcwdu()
            watch_obj = self._context.watch

            if watch_obj: # Watching is enabled.
                watched_fd = watch_obj[wd].fd
                # TODO: make 'is not None' when Watch fd changes
                if watched_fd:
                    readers.append(watched_fd)

            t = select.select(readers, writers, errs, timeout)
            ready_to_read, ready_to_write, in_error = t

            if sys.stdin.fileno() in ready_to_read:
                # TODO: do we know this is necessarily a single character? If
                #       so, how?
                self._parse_char(os.read(sys.stdin.fileno(), 1))

    def _tty_init(self):
        tty_attributes = termios.tcgetattr(sys.stdin.fileno())

        # Save tty settings, and initiate a trigger to restore them at exit.
        # Need to create a copy of tty_attributes so that the original values
        # are preserved and not just aliased. --toc
        self._term_save = list(tty_attributes)
        atexit.register(self._restore_term)

        # Modify existing tty settings to reflect necessary varix changes.
        tty_attributes[3] &= ~(termios.ICANON | termios.ECHO)
        tty_attributes[6][termios.VMIN] = 1
        tty_attributes[6][termios.VTIME] = 0

        # Set the modified tty settings in the stdin tty.
        fd = sys.stdin.fileno()
        when = termios.TCSANOW
        attributes = tty_attributes
        termios.tcsetattr(fd, when, attributes)

        logging.info('%s initialized input tty' % classname(self))
